/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


/*************************************************************************
 *\author: starlove
 *************************************************************************/
#ifndef __CC_PYTHON_IMPORT_UTILS__
#define __CC_PYTHON_IMPORT_UTILS__

NS_CCPY_BEGIN

	typedef struct __CPyClassBase
	{
		PyObject_HEAD
		static void __del__(__CPyClassBase *self)
		{
			self->ob_type->tp_free((PyObject*)self);
		}
		static PyMethodDef *__methods__;
		static PyGetSetDef *__getsets__;
		//static PyMemberDef *__members__;
		static const char *__name__;
		static const char *__doc__;
		static PyObject *__bases__;

		template<typename T>
		static PyTypeObject *GetTypeObject()
		{
			static PyTypeObject typeinfo = {
				//PyVarObject_HEAD_INIT(0, 0)
				PyObject_HEAD_INIT(&PyType_Type)
				0,								/*ob_size*/
				T::__name__,					/*tp_name*/
				sizeof(T),						/*tp_basicsize*/
				0,								/*tp_itemsize*/
				(destructor)T::__del__,			/*tp_dealloc*/
			};
			return &typeinfo;
		}

		template<typename T>
		static PyTypeObject *GetType()
		{
			PyTypeObject *pType = GetTypeObject<T>();
			pType->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
			pType->tp_doc = T::__doc__;
			pType->tp_methods = T::__methods__;
			pType->tp_getset = T::__getsets__;
			//pType->tp_members = T::__members__;
			pType->tp_new = T::__new__;
			pType->tp_init = (initproc)T::__init__;
			if(T::__bases__ != NULL)
				pType->tp_bases = T::__bases__;
			pType->tp_str = NULL;
			pType->tp_repr = NULL;

			if( (pType->tp_flags & Py_TPFLAGS_READY) != Py_TPFLAGS_READY)
			{
				PyType_Ready(pType);
			}
			Py_XINCREF(pType);
			return pType;
		};
	}CPyClassBase, *LPCPyClassBase;

	struct CPyArrayBase
	{
		PyObject_HEAD
		Py_ssize_t __count__;
		static Py_ssize_t __len__(CPyArrayBase *pySelf);

		template<typename T>
		static PyTypeObject *GetType()
		{
			static PySequenceMethods psm = {
				(lenfunc)__len__,
				0,
				0,
				(ssizeargfunc)T::__getitem__,
				0,
				(ssizeobjargproc)T::__setitem__,
			};
			static PyTypeObject typeinfo = {
				PyObject_HEAD_INIT(&PyType_Type)
				0,								/*ob_size*/
				T::__name__,					/*tp_name*/
				sizeof(T),						/*tp_basicsize*/
				0,								/*tp_itemsize*/
				(destructor)T::__del__,
			};
			typeinfo.tp_as_sequence = &psm;
			typeinfo.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
			typeinfo.tp_doc = T::__name__;
			typeinfo.tp_str = (reprfunc)T::__repr__;
			typeinfo.tp_repr = (reprfunc)T::__repr__;
			PyType_Ready(&typeinfo);
			Py_INCREF(&typeinfo);
			return &typeinfo;
		}
	};

	class CPyModule
	{
	public:
		CPyModule(const char *module_name, const char *module_doc = NULL)
		{
			static LPCPyClassBase pClassBase = NULL;
			if(!pClassBase)
			{
    			PyTypeObject *pType = CPyClassBase::GetTypeObject<CPyClassBase>();
				if( (pType->tp_flags & Py_TPFLAGS_READY) != Py_TPFLAGS_READY)
				{
					PyType_Ready(pType);
				}
				Py_INCREF(pType);
				pClassBase = PyObject_NEW(CPyClassBase, pType);
				Py_INCREF(pClassBase);
			}
			m_pClassBase = pClassBase;
			m_name = module_name;
			m_doc = module_doc;
			m_pMethods_ref = NULL;
		}

		void RegisterIntValue(const char *name, int v){ m_intValues.insert(std::pair<const char *,int>(name, v)); };
		void RegisterCallable(const char *name, PyCFunction pyFunc, int flag, const char *doc = NULL)
		{
			PyMethodDef pd;
			pd.ml_name = name;
			pd.ml_meth = pyFunc;
			pd.ml_doc = doc;
			pd.ml_flags = flag;
			m_methods.push_back(pd);
		}

		void Embed(const char *parent = NULL, const char *self_name = NULL)
		{
			static PyMethodDef G_NONEDEF = {NULL,NULL,0,NULL};
			unsigned int nmsize = m_methods.size();
			m_pMethods_ref = NULL;
			if(nmsize > 0)
			{
				PyMethodDef *pMethods = new PyMethodDef[nmsize + 1];
				m_pMethods_ref = pMethods;
				unsigned int n = 0;
				std::vector<PyMethodDef>::iterator it = m_methods.begin();
				while(it != m_methods.end() && n < nmsize)
				{
					memcpy(pMethods, &(*it), sizeof(PyMethodDef));
					n ++;
					it ++;
					pMethods ++;
				}
				memcpy(pMethods, &G_NONEDEF, sizeof(PyMethodDef));
			}

			PyObject* pObjMod = Py_InitModule4(m_name, m_pMethods_ref, m_doc, (PyObject *)CPyModule::GetPyClassBase(), PYTHON_API_VERSION);
			if( !pObjMod )
				return;
			if(parent)
			{
				PyObject *parentMod = PyImport_ImportModule(parent);
				if(parentMod)
				{
					PyModule_AddObject(parentMod, self_name, pObjMod);
					Py_XINCREF(pObjMod);
				}
			}

			std::map<const char *, int>::iterator itV = m_intValues.begin();
			while(itV != m_intValues.end())
			{
				PyModule_AddIntConstant(pObjMod,itV->first,itV->second);
				itV ++;
			}
			std::map<const char *, PyTypeObject *>::iterator itCls = m_cls.begin();
			while(itCls != m_cls.end())
			{
				PyModule_AddObject(pObjMod,itCls->first,(PyObject *)itCls->second);
				itCls ++;
			}
		}
		template <typename T>
		void RegisterClass()
		{
			PyTypeObject *pyType = CPyClassBase::GetType<T>();
			Py_XINCREF(pyType);
			m_cls.insert(std::pair<const char *, PyTypeObject *>(T::__name__, pyType));
		};
		static LPCPyClassBase GetPyClassBase(){ return m_pClassBase; };
	private:
		const char *m_name;
		const char *m_doc;
		PyMethodDef *m_pMethods_ref;
		std::vector<PyMethodDef> m_methods;
		std::map<const char *, int> m_intValues;
		std::map<const char *, PyTypeObject *> m_cls;
		static LPCPyClassBase m_pClassBase;
	};
NS_CCPY_END

#endif